﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace FATLib
{
    public class LowLevelDiskStream : Stream
    {
        private IntPtr handle;

        public LowLevelDiskStream( char drive_letter )
        {
            var sb = new StringBuilder();
            sb.Append( @"\\.\" );
            sb.Append( drive_letter );
            sb.Append( ":" );
            handle = CreateFile( sb.ToString(), -1073741824, 3, 0, 3, 128, IntPtr.Zero );
        }

        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return false; }
        }

        public override void Flush()
        {
            throw new NotSupportedException();
        }

        public override long Length
        {
            get
            {
                long val;
                GetFileSizeEx( handle, out val );
                return val;
            }
        }

        public override long Position
        {
            get
            {
                long value;
                SetFilePointerEx( handle, 0, out value, 1 );
                return value;
            }
            set
            {
                Seek( value, SeekOrigin.Begin );
            }
        }

        public override int Read( byte[] buffer, int offset, int count )
        {
            int read;
            bool success = ReadFile( handle, buffer, count, out read, 0 );
            return read;
        }

        public override long Seek( long offset, SeekOrigin origin )
        {
            uint seek_origin;
            switch( origin ) { case SeekOrigin.Begin: seek_origin = 0; break; case SeekOrigin.Current: seek_origin = 1; break; case SeekOrigin.End: seek_origin = 2; break; default: return 0; }
            long outp;
            SetFilePointerEx( handle, offset, out outp, seek_origin );
            return outp;
        }

        public override void SetLength( long value )
        {
            throw new NotSupportedException();
        }

        public override void Write( byte[] buffer, int offset, int count )
        {
            throw new NotSupportedException();
        }

        protected override void Dispose( bool disposing )
        {
            CloseHandle( handle );
            base.Dispose( disposing );
        }

        [DllImport( "kernel32.dll", SetLastError = true )]
        private static extern IntPtr CreateFile( string lpFileName, Int32 dwDesiredAccess,
            Int32 dwShareMode, Int32 lpSecurityAttributes, Int32 dwCreationDisposition,
            Int32 dwFlagsAndAttributes, IntPtr hTemplateFile );

        [DllImport( "kernel32.dll" )]
        private static extern bool ReadFile( IntPtr hFile, byte[] lpBuffer,
           int nNumberOfBytesToRead, out int lpNumberOfBytesRead, uint lpOverlapped );

        [DllImport( "kernel32.dll" )]
        private extern static int GetFileSizeEx( IntPtr hFile, out long lpFileSize );

        [DllImport( "kernel32.dll" )]
        private extern static int SetFilePointerEx( IntPtr hFile, long liDistanceToMove, out long lpNewFilePointer, uint dwMoveMethod );

        [DllImport( "kernel32.dll" )]
        private extern static Boolean CloseHandle( IntPtr hObject );
    }
}
